local table_insert = table.insert
local table_sort = table.sort
local table_remove = table.remove
local math_floor = math.floor
local table_unpack = table.unpack
local math_random = math.random
local math_abs = math.abs
--math.randomseed(os.time())
local util = require "util"
local zjh_rule = require "zhajinhua.zjh_rule"
local error_code = require "error_code"
local constant = require "constant"

local M = {}

local MAX_BETTING_COINS = 1000000000    --单个人押注上限

local SIGN_CLUB = 1         --梅花
local SIGN_DIAMOND = 2      --钻石
local SIGN_HEART = 3        --桃心
local SIGN_SPADE = 4        --黑桃


local RED_SIDE = 1
local BLACK_SIDE = 2
local SPECIAL_CARD_TYPE_SIDE = 3

--[[
    每张卡牌分别由1-10,J,Q,K,black joker,red jocker 定义为1-15
--]]

local GAME_STATUS_INIT = 0          --初始化状态
local GAME_STATUS_STARTED = 1       --游戏开始
local GAME_STATUS_BETTING = 2       --押注
local GAME_STATUS_OVER = 3          --结束状态
-------------------------------------------------------


local MT_INDEX = {}

--可下注列表
local AVAILABLE_BETTING_COINS_MAP = {
    [100] = true,
    [1000] = true,
    [5000] = true,
    [10000] = true,
    [50000] = true,
}

local AVAILABLE_SIDES_MAP = {
    [RED_SIDE] = true,
    [BLACK_SIDE] = true,
    [SPECIAL_CARD_TYPE_SIDE] = true
}


local COMPENSATION_TYPE_NORMAL = 0
local COMPENSATION_TYPE_BAOZI = 1
local COMPENSATION_TYPE_SHUNJIN = 2
local COMPENSATION_TYPE_JINHUA = 3
local COMPENSATION_TYPE_SHUNZI = 4
local COMPENSATION_TYPE_9A = 5

local COMPENSATION_RATE = {
    [COMPENSATION_TYPE_BAOZI] = 15,
    [COMPENSATION_TYPE_SHUNJIN] = 10,
    [COMPENSATION_TYPE_JINHUA] = 5,
    [COMPENSATION_TYPE_SHUNZI] = 3,
    [COMPENSATION_TYPE_9A] = 2,
}

local CARD_TYPE_PROBABILITY = {
    [COMPENSATION_TYPE_NORMAL] =        {1,     16900}, --高牌
    [COMPENSATION_TYPE_9A] =            {16901, 18900}, --9-A对子
    [COMPENSATION_TYPE_SHUNZI] =        {18901, 19500}, --顺子
    [COMPENSATION_TYPE_JINHUA] =        {19501, 19840}, --金花
    [COMPENSATION_TYPE_SHUNJIN] =       {19841, 19980}, --同花顺
    [COMPENSATION_TYPE_BAOZI] =         {19981, 20000}, --豹子
}

local function get_compensation_type(type,card_list)
    if type == zjh_rule.CT_BAO_ZI then
        return COMPENSATION_TYPE_BAOZI
    end
    if type == zjh_rule.CT_SHUN_JIN then
        return COMPENSATION_TYPE_SHUNJIN
    end
    if type == zjh_rule.CT_JIN_HUA then
        return COMPENSATION_TYPE_JINHUA
    end
    if type == zjh_rule.CT_SHUN_ZI then
        return COMPENSATION_TYPE_SHUNZI
    end
    
    if type == zjh_rule.CT_DOUBLE then
        local card_count_map = {}
        for _,card_id in pairs(card_list) do
            local value = zjh_rule.get_card_logic_value(card_id)
            card_count_map[value] = (card_count_map[value] or 0) + 1
        end

        for value,count in pairs(card_count_map) do
            if count >=2 and value >= 9 and value <= 14 then
                return COMPENSATION_TYPE_9A
            end
        end
    end

    return COMPENSATION_TYPE_NORMAL
end

function M.get_card_type(card_list)
    local type = zjh_rule.get_card_type(card_list)
    return get_compensation_type(type,card_list)
end

function MT_INDEX:init(gen_card_instance)
    --生成所有的牌
    local make_card_id = zjh_rule.make_card_id
    local cards = {}
    for i = 1,13 do
        for j = 1,4 do
            table_insert(cards,make_card_id(j,i))
        end
    end

    self.game_status = GAME_STATUS_INIT
    self.cards = cards
    self.rb_cards_map = {}

    self.betting_list = {
        [RED_SIDE] = 0,
        [BLACK_SIDE] = 0,
        [SPECIAL_CARD_TYPE_SIDE] = 0
    }

    self.player_betting_map = {}            --当前游戏中的玩家列表
    self.game_result = nil
    self.side_gold_list = {}
    self.gen_card_instance = gen_card_instance
    self.betting_list_no_robot = {
        [RED_SIDE] = 0,
        [BLACK_SIDE] = 0,
        [SPECIAL_CARD_TYPE_SIDE] = 0
    }
    self.basechip = nil
    self.player_bet_flottant = nil

    self.robot_betting_list = {
        [RED_SIDE] = {},
        [BLACK_SIDE] = {},
        [SPECIAL_CARD_TYPE_SIDE] = {},
    }
    return true
end

function MT_INDEX:check_bet_num(player_list,player_not_bet_num)
    local ret5 = {}
    local ret4 = {}
    for uid,info in pairs(player_list) do
        if not self.player_betting_map[uid] then
            player_not_bet_num[uid] = player_not_bet_num[uid] or 0
            player_not_bet_num[uid] = player_not_bet_num[uid] + 1
            if player_not_bet_num[uid] >= 5 then
                table_insert(ret5,uid)
            end
            if player_not_bet_num[uid] == 4 then
                table_insert(ret4,uid)
            end
        else
            player_not_bet_num[uid] = 0
        end
    end
    for uid,num in pairs(player_not_bet_num) do
        if not player_list[uid] then
            player_not_bet_num[uid] = nil
        end
    end    
    return ret5,ret4
end
function MT_INDEX:get_betting_list_no_robot()
    return self.betting_list_no_robot
end

function MT_INDEX:get_side_betting_total_coins(side)
    return self.betting_list[side] or 0
end

function MT_INDEX:get_side_player_bett_coins(side)
    return self.betting_list_no_robot[side] or 0
end

function MT_INDEX:get_side_robot_type_betting_coins(side,coins_type)
    return self.robot_betting_list[side][coins_type] or 0
end

function MT_INDEX:get_side_player_bet_list(side)
    local side_bet_list = {}
    for uid,records in pairs(self.player_betting_map) do
        if records[side] then
            table_insert(side_bet_list,{uid = uid,bet_coins = records[side].coins or 0})
        end
    end

    return side_bet_list
end

function MT_INDEX:get_total_betting_coins()
    local total_coins = 0
    for side,coins in pairs(self.betting_list) do
        total_coins = total_coins + coins
    end
    return total_coins
end

function MT_INDEX:start_game()
    assert(self.game_status == GAME_STATUS_INIT)
    self.game_status = GAME_STATUS_STARTED

    return true
end

function MT_INDEX:is_side_limit(side,coins,self_table_type)
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata then return false end

    if roomdata.difference_limit and roomdata.difference_limit ~= 0 then
        if side == RED_SIDE and math_abs(self.betting_list[side] + coins - self.betting_list[BLACK_SIDE]) > roomdata.difference_limit then
            --errlog("is_side_limit difference_limit:",roomdata.difference_limit)
            return true
        elseif side == BLACK_SIDE and math_abs(self.betting_list[side] + coins - self.betting_list[RED_SIDE]) > roomdata.difference_limit then
            --errlog("is_side_limit difference_limit:",roomdata.difference_limit)
            return true
        end
    end

    if roomdata.all_special_limit and side == SPECIAL_CARD_TYPE_SIDE and roomdata.all_special_limit ~= 0 and self.betting_list[side] + coins > roomdata.all_special_limit then
       -- errlog("is_side_limit all_special_limit:",roomdata.all_special_limit)
        return true
    end
    return false
end

function MT_INDEX:is_personal_side_limit(uid,side,coins,self_table_type)
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata then return false end
    local records = self.player_betting_map[uid] or {}
    if roomdata.red_limit and roomdata.red_limit ~= 0 and side == RED_SIDE and records[RED_SIDE] and records[RED_SIDE].coins + coins > roomdata.red_limit then
        return true
    end
    if roomdata.black_limit and roomdata.black_limit ~= 0 and side == BLACK_SIDE and records[BLACK_SIDE] and records[BLACK_SIDE].coins + coins > roomdata.black_limit then
        return true
    end
    if roomdata.special_limit and roomdata.special_limit ~= 0 and side == SPECIAL_CARD_TYPE_SIDE and records[SPECIAL_CARD_TYPE_SIDE] and records[SPECIAL_CARD_TYPE_SIDE].coins + coins > roomdata.special_limit then
        return true
    end
    return false
end

function MT_INDEX:is_personal_limit(uid,side,coins,self_table_type)
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata or not roomdata.person_limit then return false end

    if roomdata.person_limit[1] and roomdata.person_limit[1] ~= 0 and coins < roomdata.person_limit[1] then 
        return true
    end
    local all_coins = coins
    local records = self.player_betting_map[uid] or {}
    for side,info in pairs(records) do
        all_coins = all_coins + info.coins
    end
    if roomdata.person_limit[2] and roomdata.person_limit[2] ~= 0 and all_coins > roomdata.person_limit[2] then
        return true
    end
    --dbglog("is_personal_limit roomdata.person_limit[2]:",roomdata.person_limit[2],"all_coins:",all_coins)

    return false
end

function MT_INDEX:get_my_betting_list(uid)
    local player_betting_map = self.player_betting_map
    local records = player_betting_map[uid]
    if not records then
        return
    end
    
    local ret = {}
    for side,info in pairs(records) do
        ret[side] = info.coins
    end
    
    return ret
end

function MT_INDEX:get_player_bet_coins()
    local player_coins_map = {}
    for uid,records in pairs(self.player_betting_map) do
        local player_coins = 0
        for _,t in pairs(records) do
            player_coins = player_coins + t.coins
        end
        player_coins_map[uid] = player_coins
    end

    return player_coins_map
end

function MT_INDEX:is_available_betting(coins,available_betting_list) --押注金额是否有效
    for i = 1,#available_betting_list do
        if available_betting_list[i] == coins then
            return true
        end
    end
    return false
end

local function can_bet(self,uid,side,coins,available_betting_list,bet_type,self_table_type)
    --print("can_bet==============",uid,side,coins,tostring_r(available_betting_list))
    if bet_type ~= constant.BET_TYPE_CONTINUE and not self:is_available_betting(coins,available_betting_list) then
       return false,-303
    end

    if not AVAILABLE_SIDES_MAP[side] then
        return false,-301
    end

    if self:is_side_limit(side,coins,self_table_type) then
        return false, error_code.SIDE_BET_LIMIT
    end

    -- local player_betting_map = self.player_betting_map
    -- local records = player_betting_map[uid]
    -- if not records then
    --     return true
    -- end
   if self:is_personal_side_limit(uid,side,coins,self_table_type) then
        return false, error_code.PERSONAL_SIDE_BET_LIMIT
    end  

    if self:is_personal_limit(uid,side,coins,self_table_type) then
        return false, error_code.PERSONAL_BET_LIMIT
    end

    -- local both_side_flags = (1 << RED_SIDE) | (1 << BLACK_SIDE)
    -- local flags = 0
    -- for side in pairs(records) do
    --     flags = (1 << side) | flags
    -- end  

    -- flags = (1 << side) | flags
    -- if flags & both_side_flags == both_side_flags then
    --     --不可以同时投两方
    --     return false,error_code.CANT_BET_DIFFERENT_SIDE
    -- end

    return true
end

MT_INDEX.can_bet = can_bet

function MT_INDEX:bet(uid,side,coins,is_robot,player_info,bet_type,self_table_type)
    local ok,ret = self:can_bet(uid,side,coins,player_info.available_betting_list,bet_type,self_table_type)
    if not ok then
        return false,ret
    end

    local player_betting_map = self.player_betting_map
    player_betting_map[uid] = player_betting_map[uid] or {}
    local records = player_betting_map[uid]

    local r = records[side] or {coins = 0}
    records[side] = r
    local curr_coins = r.coins 
    r.coins = curr_coins + coins

    local side_coins = self.betting_list[side] + coins
    self.betting_list[side] = side_coins

    --非机器人进行累计
    if not is_robot then
        print("bet",uid,side,coins,is_robot)
        self.betting_list_no_robot[side] = self.betting_list_no_robot[side] + coins
    else
        local coins_type = player_info.coins_type
      --  print("coins_type",uid,coins_type)
        self.robot_betting_list[side][coins_type] = (self.robot_betting_list[side][coins_type] or 0) + coins
    end

    if coins >= 10000 then
        --增加金砖的记录
        local curr_gold_num = self.side_gold_list[side] or 0
        self.side_gold_list[side] = curr_gold_num + 1
    end

    return true,{
        side_coins = side_coins,
        my_side_coins = r.coins,
    }
end

function MT_INDEX:shuffle(swap_times)
    assert(self.game_status == GAME_STATUS_STARTED)
    -- local cards = self.cards
    
    -- swap_times = swap_times or 3
    -- local card_count = #cards
    -- for j=1,swap_times do
    --     for i = 1,card_count do
    --         local n = math_random(1,card_count)
    --         local m = math_random(1,card_count)
    --         if n ~= m then
    --             cards[n],cards[m] = cards[m],cards[n]
    --         end
    --     end
    -- end

    return true
end

function MT_INDEX:deal()
    assert(self.game_status == GAME_STATUS_STARTED)
    -- local cards = self.cards
    -- local rb_cards_map = self.rb_cards_map
    -- assert(cards and rb_cards_map)

    -- for k = RED_SIDE,BLACK_SIDE do
    --     local card_list = {}
    --     rb_cards_map[k] = card_list
    --     for i = 1,zjh_rule.MAX_CARD_COUNT do
    --         table_insert(card_list,assert(table_remove(cards)))
    --     end
    -- end

    -- local is_draw = zjh_rule.is_draw
    -- --防止平局
    -- local red_cards = rb_cards_map[RED_SIDE]
    -- local black_cards = rb_cards_map[BLACK_SIDE]
    -- while #cards > 0 and is_draw(red_cards,black_cards) do
    --     print('now is draw...',tostring_r(rb_cards_map))
    --     red_cards[1] = assert(table_remove(cards))
    -- end
    return true
end

function MT_INDEX:start_betting()
    assert(self.game_status == GAME_STATUS_STARTED)
    self.game_status = GAME_STATUS_BETTING
    return true
end

function MT_INDEX:is_betting()
    return self.game_status == GAME_STATUS_BETTING
end

function MT_INDEX:is_game_over()
    return self.game_status == GAME_STATUS_OVER
end

function MT_INDEX:get_betting_uid_list()
    local betting_uid_list = {}
    for uid,_ in pairs(self.player_betting_map) do
        table_insert(betting_uid_list,uid)
    end
    return betting_uid_list
end

function MT_INDEX:get_all_betting_list()
    local ret = {}
    for side,coins in pairs(self.betting_list) do
        ret[side] = coins
    end
    
    return ret
end    

function MT_INDEX:get_gold_list()
    local ret = {}
    for side,gold_num in pairs(self.side_gold_list) do
        ret[side] = gold_num
    end
    return ret
end

function MT_INDEX:load_other_cnf(basechip,flottant)
    self.basechip               = basechip
    self.player_bet_flottant    = math_random(-flottant,flottant)
end

local function copy_table(t)
    local new_t = {}
    for k,v in pairs(t) do
        new_t[k] = v
    end
    return new_t
end

function MT_INDEX:judge_result(fee_percent,affect_side)
    assert(self.game_status == GAME_STATUS_BETTING)

    self.game_status = GAME_STATUS_OVER

    --加入到比牌列表中
   -- print("judge_result",fee_percent,affect_side)
    local red_card_list,black_card_list = self.gen_card_instance:get_side_cards(affect_side)

    -- local rb_cards_map = self.rb_cards_map
    -- local red_card_list = assert(rb_cards_map[RED_SIDE])
    -- local black_card_list = assert(rb_cards_map[BLACK_SIDE])
    local red_type = zjh_rule.get_card_type(red_card_list)
    local black_type = zjh_rule.get_card_type(black_card_list)

    local win = zjh_rule.compare_card(red_type,red_card_list,black_type,black_card_list)

    if red_type == zjh_rule.CT_SPECIAL then
        red_type = zjh_rule.CT_SINGLE
    end
    if black_type == zjh_rule.CT_SPECIAL then
        black_type = zjh_rule.CT_SINGLE
    end

    local winner_info = {
        side = BLACK_SIDE,
        type = black_type,
        card_list = black_card_list,
    }

    if win then
        winner_info.side = RED_SIDE
        winner_info.type = red_type
        winner_info.card_list = red_card_list
    end

    local compensation_type = get_compensation_type(winner_info.type,winner_info.card_list)
    local compensation_map = {}
    local pay_fee_map = {}
    local winner_coin_map = {}
    local player_side_map = {}

    for uid,records in pairs(self.player_betting_map) do
        compensation_map[uid] = 0
        pay_fee_map[uid] = 0
        winner_coin_map[uid] = 0
        player_side_map[uid] = {}

        for side,r in pairs(records) do
            if side == winner_info.side then
                local fee = math_floor(r.coins * fee_percent)
                local award_coin = r.coins
                compensation_map[uid] = compensation_map[uid] + award_coin - fee
                pay_fee_map[uid] = pay_fee_map[uid] + fee
                winner_coin_map[uid] = winner_coin_map[uid] + award_coin + r.coins - fee
                table_insert(player_side_map[uid],{side = side,result_coins = award_coin})
            elseif side == SPECIAL_CARD_TYPE_SIDE and compensation_type ~= COMPENSATION_TYPE_NORMAL then
                local rate = COMPENSATION_RATE[compensation_type]
                local fee = math_floor(r.coins * rate * fee_percent)
                local award_coin = r.coins * rate
                compensation_map[uid] = compensation_map[uid] + award_coin - fee
                pay_fee_map[uid] = pay_fee_map[uid] + fee
                winner_coin_map[uid] = winner_coin_map[uid] + award_coin + r.coins - fee
                table_insert(player_side_map[uid],{side = side,result_coins = award_coin})
            else
                compensation_map[uid] = compensation_map[uid] - r.coins
                table_insert(player_side_map[uid],{side = side,result_coins = -r.coins})
            end
        end
    end

    -- --台费
    -- for uid,coins in pairs(compensation_map) do
    --     if coins > 0 then
    --         local fee = math_floor(coins * fee_percent)
    --         compensation_map[uid] = compensation_map[uid] - fee
    --         winner_coin_map[uid] = winner_coin_map[uid] - fee
    --         pay_fee_map[uid] = fee
    --     end
    -- end

    self.game_result = {
        winner_side       = winner_info.side,
        winner_type       = winner_info.type,
        red_type          = red_type,
        red_card_list     = red_card_list,
        black_type        = black_type,
        black_card_list   = black_card_list,
        compensation_type = compensation_type,
        compensation_map  = compensation_map,
        pay_fee_map       = pay_fee_map,
        winner_coin_map   = winner_coin_map,
        player_side_map   = player_side_map,
    }
  --  dbglog("hhdz_game_result",winner_info.side,winner_info.type,red_type,black_type,tostring_r(red_card_list),tostring_r(black_card_list))

    return true
end

function MT_INDEX:get_game_result()
    assert(self.game_status == GAME_STATUS_OVER)
    return self.game_result
end

function MT_INDEX:get_special_rate()
    local baozi_range = CARD_TYPE_PROBABILITY[COMPENSATION_TYPE_BAOZI]
    local duizi_range = CARD_TYPE_PROBABILITY[COMPENSATION_TYPE_9A]
    return math_floor((baozi_range[2] - duizi_range[1]) / baozi_range[2] * 100)
end

M.AVAILABLE_BETTING_COINS_MAP = AVAILABLE_BETTING_COINS_MAP
M.AVAILABLE_SIDES_MAP = AVAILABLE_SIDES_MAP

M.COMPENSATION_RATE = COMPENSATION_RATE

function M.new()
    local o = {}
    return setmetatable(o,{__index = MT_INDEX})
end

local ALL_SIDES = {RED_SIDE,BLACK_SIDE}
local ALL_COMPENSATION_TYPES = {
    COMPENSATION_TYPE_NORMAL,
    COMPENSATION_TYPE_BAOZI,
    COMPENSATION_TYPE_SHUNJIN,
    COMPENSATION_TYPE_JINHUA,
    COMPENSATION_TYPE_SHUNZI,
    COMPENSATION_TYPE_9A,
}

local CT_SINGLE   = 1   --单牌类型
local CT_DOUBLE   = 2   --对子类型
local CT_SHUN_ZI  = 3   --顺子类型
local CT_JIN_HUA  = 4   --金花类型
local CT_SHUN_JIN = 5   --顺金类型
local CT_BAO_ZI   = 6   --豹子类型

local ALL_TYPES = {
    CT_SINGLE,
    CT_DOUBLE,
    CT_SHUN_ZI,
    CT_JIN_HUA,
    CT_SHUN_JIN,
    CT_BAO_ZI,
}

function M.get_random_history(num)
    local ret = {}
    for i = 1,num do
            local randnum = math_random(1,10000)
            local value = 0
            local t = 1
            if randnum <= 15 then
                value = ALL_COMPENSATION_TYPES[COMPENSATION_TYPE_SHUNJIN]
                t = CT_SHUN_JIN
            elseif randnum <= 45 then
                value = ALL_COMPENSATION_TYPES[COMPENSATION_TYPE_BAOZI]
                t = CT_BAO_ZI
            elseif randnum <= 291 then
                value = ALL_COMPENSATION_TYPES[COMPENSATION_TYPE_SHUNZI]
                t = CT_SHUN_ZI
            elseif randnum <= 891 then
                value = ALL_COMPENSATION_TYPES[COMPENSATION_TYPE_JINHUA]
                t = CT_JIN_HUA
            elseif randnum <= 1491 then
                value = ALL_COMPENSATION_TYPES[COMPENSATION_TYPE_9A]
                t = CT_DOUBLE
            else 
                value = ALL_COMPENSATION_TYPES[COMPENSATION_TYPE_NORMAL]
                t = CT_SINGLE
            end

            table_insert(ret,{
            side = ALL_SIDES[math_random(1,#ALL_SIDES)],
            type = t, -- ALL_TYPES[math_random(1,#ALL_TYPES)],
            compensation_type = value,

           -- compensation_type = ALL_COMPENSATION_TYPES[math_random(1,#ALL_COMPENSATION_TYPES)],
        })
    end
    return ret
end

--local AVAILABLE_BETTING_COINS_LIST = {}
--local ROBOT_AVAILABLE_BETTING_COINS_LIST = {}
--for coins in pairs(AVAILABLE_BETTING_COINS_MAP) do
--    table_insert(AVAILABLE_BETTING_COINS_LIST,coins)
--    if coins <= 100000 then
--        table_insert(ROBOT_AVAILABLE_BETTING_COINS_LIST,coins)
--    end
--end

--M.AVAILABLE_BETTING_COINS_LIST = AVAILABLE_BETTING_COINS_LIST
--M.ROBOT_AVAILABLE_BETTING_COINS_LIST = ROBOT_AVAILABLE_BETTING_COINS_LIST
M.CARD_TYPE_PROBABILITY = CARD_TYPE_PROBABILITY
M.RED_SIDE = RED_SIDE
M.BLACK_SIDE = BLACK_SIDE
M.RICH_SEAT_SIDE = 1
M.LUCK_SEAT_SIDE = 4

return M